using UnityEngine;
using System.Collections;

public class PlatformerController : MonoBehaviour
{

    // Does this script currently respond to Input?
    public bool canControl = true;
    public AudioClip audioClipWalk;
    public AudioClip audioClipJump;

    // The character will spawn at spawnPoint's position when needed.  This could be changed via a script at runtime to implement, e.g. waypoints/savepoints.
    public Transform spawnPoint;
    //var sound :JukeBoxController = GetComponent("JukeBoxController");


    public PlatformerControllerMovement movement;// = new PlatformerControllerMovement();
    public ModifySoundController modifySoundController = new ModifySoundController();
    // We will contain all the jumping related variables in one helper class for clarity.
    

    public PlatformerControllerJumping jump = new PlatformerControllerJumping();

    private CharacterController controller = new CharacterController();
    private SpriteAnimation spriteAnimation;

    // Moving platform support.
    //private Transform activePlatform = new Transform();
    private Vector3 activeLocalPlatformPoint = new Vector3();
    private Vector3 activeGlobalPlatformPoint = new Vector3();
    private Vector3 lastPlatformVelocity = new Vector3();

    // This is used to keep track of special effects in UpdateEffects ();
    private bool areEmittersOn = false;

    public void Awake()
    {
    	movement = this.gameObject.AddComponent<PlatformerControllerMovement>();
//    	jump = this.gameObject.AddComponent<PlatformerControllerJumping>();
    	
        movement.direction = transform.TransformDirection(Vector3.forward);
        controller = GetComponent<CharacterController>();
        this.spriteAnimation = GetComponentInChildren<SpriteAnimation>();
        Spawn();
    }

    public void Spawn()
    {
        // reset the character's speed
        movement.verticalSpeed = 0.0F;
        movement.speed = 0.0F;

        // reset the character's position to the spawnPoint
        transform.position = spawnPoint.position;

    }

    public void OnDeath()
    {
        Spawn();
    }

    public void UpdateSmoothedMovementDirection()
    {
        float h = Input.GetAxisRaw("Horizontal");

        if (!canControl)
            h = 0.0F;

        movement.isMoving = Mathf.Abs(h) > 0.1;

        if (movement.isMoving)
            movement.direction = new Vector3(h, 0, 0);

        // Grounded controls
        if (controller.isGrounded)
        {
            // Smooth the speed based on the current target direction
            float curSmooth = movement.speedSmoothing * Time.deltaTime;

            // Choose target speed
            float targetSpeed = Mathf.Min(Mathf.Abs(h), 1.0F);

            // Pick speed modifier
            if (Input.GetButton("Fire2") && canControl)
                targetSpeed *= movement.runSpeed;
            else
                targetSpeed *= movement.walkSpeed;

            movement.speed = Mathf.Lerp(movement.speed, targetSpeed, curSmooth);

            movement.hangTime = 0.0F;
        }
        else
        {
            // In air controls
            movement.hangTime += Time.deltaTime;
            if (movement.isMoving)
                movement.inAirVelocity += new Vector3(Mathf.Sign(h), 0, 0) * Time.deltaTime * movement.inAirControlAcceleration;
        }
    }

    public void FixedUpdate()
    {
        // Make sure we are absolutely always in the 2D plane.
        transform.position = new Vector3(this.transform.position.x, this.transform.position.y, 0);

    }

    public void ApplyJumping()
    {
        // Prevent jumping too fast after each other
        if (jump.lastTime + jump.repeatTime > Time.time)
            return;

        if (controller.isGrounded)
        {
            // Jump
            // - Only when pressing the button down
            // - With a timeout so you can press the button slightly before landing  
            if (jump.enabled && Time.time < jump.lastButtonTime + jump.timeout)
            {
                movement.verticalSpeed = CalculateJumpVerticalSpeed(jump.height);
                movement.inAirVelocity = lastPlatformVelocity;
                SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
            }
        }
    }

    public void ApplyGravity()
    {
        // Apply gravity
        bool jumpButton = Input.GetButton("Jump");



        if (!canControl)
            jumpButton = false;

        // When we reach the apex of the jump we send out a message
        if (jump.jumping && !jump.reachedApex && movement.verticalSpeed <= 0.0)
        {
            jump.reachedApex = true;
            SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
        }

        // if we are jumping and we press jump button, we do a double jump or
        // if we are falling, we can do a double jump to
        if ((jump.jumping && Input.GetButtonUp("Jump") && !jump.doubleJumping) || (!controller.isGrounded && !jump.jumping && !jump.doubleJumping && movement.verticalSpeed < -12.0))
        {
            jump.canDoubleJump = true;
        }

        // if we can do a double jump, and we press the jump button, we do a double jump
        if (jump.canDoubleJump && Input.GetButtonDown("Jump") && !IsTouchingCeiling())
        {
            jump.doubleJumping = true;
            movement.verticalSpeed = CalculateJumpVerticalSpeed(jump.doubleJumpHeight);
            jump.canDoubleJump = false;

        }
        // * When jumping up we don't apply gravity for some time when the user is holding the jump button
        //   This gives more control over jump height by pressing the button longer
        bool extraPowerJump = jump.jumping && !jump.doubleJumping && movement.verticalSpeed > 0.0 && jumpButton && transform.position.y < jump.lastStartHeight + jump.extraHeight && !IsTouchingCeiling();

        if (extraPowerJump)
            return;
        else if (controller.isGrounded)
        {
            movement.verticalSpeed = -movement.gravity * Time.deltaTime;
            jump.canDoubleJump = false;
        }
        else
            movement.verticalSpeed -= movement.gravity * Time.deltaTime;

        // Make sure we don't fall any faster than maxFallSpeed.  This gives our character a terminal velocity.
        movement.verticalSpeed = Mathf.Max(movement.verticalSpeed, -movement.maxFallSpeed);
    }

    public float CalculateJumpVerticalSpeed(float targetJumpHeight)
    {
        // From the jump height and gravity we deduce the upwards speed
        // for the character to reach at the apex.

        return Mathf.Sqrt(2 * targetJumpHeight * movement.gravity);
    }

    public void DidJump()
    {
        jump.jumping = true;
        jump.reachedApex = false;
        jump.lastTime = Time.time;
        jump.lastStartHeight = transform.position.y;
        jump.lastButtonTime = -10;
    }

    public void UpdateEffects()
    {
        bool wereEmittersOn = areEmittersOn;
        areEmittersOn = jump.jumping && movement.verticalSpeed > 0.0;

        // By comparing the previous value of areEmittersOn to the new one, we will only update the particle emitters when needed
        if (wereEmittersOn != areEmittersOn)
        {

            foreach (ParticleEmitter emitter in GetComponentsInChildren<ParticleEmitter>())
            {
                emitter.emit = areEmittersOn;
            }
        }
    }

    public void Update()
    {
        if (Input.GetButtonDown("Jump") && canControl)
        {
            jump.lastButtonTime = Time.time;
            if (!jump.jumping)
            {
                audio.clip = audioClipJump;
                audio.volume = 1.0F * modifySoundController.baseWalkAudioVolume;
                audio.pitch = Random.Range(1.0F - modifySoundController.soundEffectPitchRandomness, 1.0F + modifySoundController.soundEffectPitchRandomness);
                audio.Play();
            }
        }

        if (canControl)
        {
            if (Input.GetButtonDown("Sound1"))
            {

                modifySoundController.sound1 = true;
            }
            else if (Input.GetButtonDown("Sound2"))
            {

                modifySoundController.sound2 = true;
            }
            else if (Input.GetButtonDown("Sound3"))
            {

                modifySoundController.sound3 = true;
            }
            else if (Input.GetButtonDown("Sound4"))
            {

                modifySoundController.sound4 = true;
            }
            if (Input.GetAxisRaw("Vertical") < 0)
            {
                movement.crouching = true;
            }
            //        if(Input.GetButtonDown("Horizontal")){
            //            audio.clip = modifySoundController.audioClipWalk;
            //		    audio.volume = 1.0 * modifySoundController.baseWalkAudioVolume;
            //		    audio.pitch = Random.Range(1.0 - modifySoundController.soundEffectPitchRandomness, 1.0 + modifySoundController.soundEffectPitchRandomness);
            //		    audio.Play();
            //        }
            if (Input.GetAxisRaw("Horizontal") > 0)
            {
                movement.walkingRight = true;
            }
            else if (Input.GetAxisRaw("Horizontal") < 0)
            {
                movement.walkingLeft = true;
            }
            else
            {
                if (Input.GetButtonDown("Modify1"))
                {


                    this.audio.clip = audioClipWalk;
                    this.audio.volume = 1.0F * modifySoundController.baseWalkAudioVolume;
                    this.audio.pitch = Random.Range(1.0F - modifySoundController.soundEffectPitchRandomness, 1.0F + modifySoundController.soundEffectPitchRandomness);
                    this.audio.Play();
                    print("Modify1");
                    modifySoundController.modifyVal = true;
                    modifySoundController.modify1 = true;
                }
                else if (Input.GetButtonDown("Modify2"))
                {
                    print("Modify2");
                    modifySoundController.modifyVal = true;
                    modifySoundController.modify2 = true;
                }
                else if (Input.GetButtonDown("Modify3"))
                {
                    print("Modify3");
                    modifySoundController.modifyVal = true;
                    modifySoundController.modify3 = true;
                }
                else if (Input.GetButtonDown("Modify4"))
                {
                    print("Modify4");
                    modifySoundController.modifyVal = true;
                    modifySoundController.modify4 = true;
                }
            }
            if (movement.crouching && (movement.walkingRight || movement.walkingLeft))
            {
                movement.creeping = true;
            }
        }

        UpdateSmoothedMovementDirection();

        // Apply gravity
        // - extra power jump modifies gravity
        ApplyGravity();

        // Apply jumping logic
        ApplyJumping();

        // Moving platform support
        //if (activePlatform != null)
        //{
        //    Vector3 newGlobalPlatformPoint = activePlatform.TransformPoint(activeLocalPlatformPoint);
        //    Vector3 moveDistance = (newGlobalPlatformPoint - activeGlobalPlatformPoint);
        //    transform.position = transform.position + moveDistance;
        //    lastPlatformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;
        //}
        //else
        //{
        //    lastPlatformVelocity = Vector3.zero;
        //}

        //activePlatform = null;

        // Save lastPosition for velocity calculation.
        Vector3 lastPosition = transform.position;

        // Calculate actual motion
        Vector3 currentMovementOffset = movement.direction * movement.speed + new Vector3(0, movement.verticalSpeed, 0) + movement.inAirVelocity;

        // We always want the movement to be framerate independent.  Multiplying by Time.deltaTime does this.
        currentMovementOffset *= Time.deltaTime;

        // Move our character!
        movement.collisionFlags = controller.Move(currentMovementOffset);

        // Calculate the velocity based on the current and previous position. 
        // This means our velocity will only be the amount the character actually moved as a result of collisions.
        movement.velocity = (transform.position - lastPosition) / Time.deltaTime;

        // Moving platforms support
        //if (activePlatform != null)
        //{
        //    activeGlobalPlatformPoint = transform.position;
        //    activeLocalPlatformPoint = activePlatform.InverseTransformPoint(transform.position);
        //}

        // Set rotation to the move direction   
        //if (movement.direction.sqrMagnitude > 0.01)
        //    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement.direction), Time.deltaTime * movement.rotationSmoothing);

        // We are in jump mode but just became grounded
        if (controller.isGrounded)
        {
            movement.inAirVelocity = Vector3.zero;
            if (jump.jumping)
            {
                jump.jumping = false;
                jump.doubleJumping = false;
                jump.canDoubleJump = false;
                SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);

                Vector3 jumpMoveDirection = movement.direction * movement.speed + movement.inAirVelocity;
                if (jumpMoveDirection.sqrMagnitude > 0.01)
                    movement.direction = jumpMoveDirection.normalized;
            }
        }

        // Update special effects like rocket pack particle effects
        UpdateEffects();

        if (movement.creeping)
        {
        	if(movement.walkingRight) {
//          		this.spriteAnimation.PlayCreepRightAnimation();
           	} else {
//           		this.spriteAnimation.PlayCreepLeftAnimation();
           	}
        }
        else
        {
            if (jump.jumping)
            {
                // this.spriteAnimation.PlayJumpAnimation();
            }
            else if (movement.crouching)
            {
                // this.spriteAnimation.PlayCrouchingAnimation();
            }
            else if (movement.walkingRight || movement.walkingLeft)
            {
            	if (movement.walkingRight)
                {
                    this.movement.walkingLeft = false;
                    this.movement.walkingRight = true;
                    this.spriteAnimation.PlayWalkRightAnimation();
                }
                else if (movement.walkingLeft)
                {
                    this.movement.walkingLeft = true;
                    this.movement.walkingRight = false;
                    this.spriteAnimation.PlayWalkLeftAnimation();
                }
            	
                if (!this.audio.isPlaying)
                {
                    audio.clip = audioClipWalk;
                    audio.volume = 1.0F * modifySoundController.baseWalkAudioVolume;
                    audio.pitch = Random.Range(1.0F - modifySoundController.soundEffectPitchRandomness, 1.0F + modifySoundController.soundEffectPitchRandomness);
                    //audio.loop = true;
                    audio.Play();
                }
            }
            else if (!modifySoundController.modifyVal)
            {
            	this.spriteAnimation.PlayIdleAnimation();
            }
        }
        modifySoundController.modify1 = false;
        modifySoundController.modify2 = false;
        modifySoundController.modify3 = false;
        modifySoundController.modify4 = false;

        modifySoundController.sound1 = false;
        modifySoundController.sound2 = false;
        modifySoundController.sound3 = false;
        modifySoundController.sound4 = false;

        modifySoundController.modifyVal = false;
        movement.creeping = false;
        movement.crouching = false;
        movement.walkingRight = false;
        movement.walkingLeft = false;
    }

    public void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (hit.moveDirection.y > 0.01F)
            return;

        // Make sure we are really standing on a straight platform
        // Not on the underside of one and not falling down from it either!
        if (hit.moveDirection.y < -0.9 && hit.normal.y > 0.9)
        {
            //activePlatform = hit.collider.transform;
        }
    }

    // Various helper functions below:
    public float GetSpeed()
    {
        return movement.speed;
    }

    public Vector3 GetVelocity()
    {
        return movement.velocity;
    }


    public bool IsMoving()
    {
        return movement.isMoving;
    }

    public bool IsJumping()
    {
        return jump.jumping;
    }

    public bool IsDoubleJumping()
    {
        return jump.doubleJumping;
    }

    public bool canDoubleJump()
    {
        return jump.canDoubleJump;
    }

    public bool IsTouchingCeiling()
    {
        return (movement.collisionFlags & CollisionFlags.CollidedAbove) != 0;
    }

    public Vector3 GetDirection()
    {
        return movement.direction;
    }

    public float GetHangTime()
    {
        return movement.hangTime;
    }

    public void Reset()
    {
        gameObject.tag = "Player";
    }

    public void SetControllable(bool controllable)
    {
        canControl = controllable;
    }

    // Require a character controller to be attached to the same game object

}
